package com.hksj.jetlinks.serialize;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class SmartDateFormatter implements DateFormatter {
    static Part year_cn = str("年");
    static Part month_cn = str("月");
    static Part day_cn = str("日");
    static Part hour_cn = str("时");
    static Part minute_cn = str("分");
    static Part second_cn = str("秒");
    static Part weeks3 = new Week("EEE", 3);
    static Part month3 = new Month("MMM", 3);
    static Part zzz = new TimezonePart("zzz", 3);
    static Part strike = str("-");
    static Part slash = str("/");
    static Part plus = str("+");
    static Part blankSpace = str(" ");
    static Part colon = str(":");
    static Part yyyy = new NumberPart("yyyy", 1900, 2999);
    static Part MM = new NumberPart("MM", 1, 12);
    static Part M = new NumberPart("M", 1, 12);
    static Part dd = new NumberPart("dd", 1, 31);
    static Part d = new NumberPart("d", 1, 31);
    static Part HH = new NumberPart("HH", 0, 23);
    static Part k = new NumberPart("k", 1, 24);
    static Part K = new NumberPart("K", 0, 11);
    static Part a = new MatchPart("a", 2, new HashSet(Arrays.asList("AM", "PM")));
    static Part h = new NumberPart("h", 1, 12);
    static Part H = new NumberPart("H", 0, 24);
    static Part mm = new NumberPart("mm", 0, 60);
    static Part m = new NumberPart("m", 0, 60);
    static Part ss = new NumberPart("ss", 0, 60);
    static Part SSS = new NumberPart("SSS", 3, 0, 999);
    static Part s = new NumberPart("s", 0, 60);
    static Part Z = new NumberPart("Z", 4, 0, 9999);
    static Part XXX = new ZoneOffsetPart();
    static Part T = str("T");
    DateTimeFormatter pattern;
    final String patternString;
    private final Part[] parts;
    private final int max;
    private final int min;

    public SmartDateFormatter withLocal(Locale locale) {
        this.pattern = this.pattern.withLocale(locale);
        return this;
    }

    SmartDateFormatter(String pattern, int max, int min, Part[] parts) {
        this.parts = parts;
        this.max = max;
        this.min = min;
        this.patternString = pattern;
        this.pattern = DateTimeFormatter.ofPattern(this.patternString);
    }

    SmartDateFormatter(String pattern, Part[] parts) {
        this.parts = parts;
        this.max = Stream.of(this.parts).mapToInt(Part::length).sum();
        this.min = this.max;
        this.patternString = pattern;
        this.pattern = DateTimeFormatter.ofPattern(this.patternString);
    }

    SmartDateFormatter(Part[] parts) {
        this.parts = parts;
        this.max = Stream.of(this.parts).mapToInt(Part::length).sum();
        this.min = this.max;
        this.patternString = (String)Stream.of(this.parts).map(Part::pattern).collect(Collectors.joining());
        this.pattern = DateTimeFormatter.ofPattern(this.patternString);
    }

    public static SmartDateFormatter of(String pattern, Part... parts) {
        return new SmartDateFormatter(pattern, parts);
    }

    public static SmartDateFormatter of(String pattern, int min, int max, Part... parts) {
        return new SmartDateFormatter(pattern, max, min, parts);
    }

    public static SmartDateFormatter of(Part... parts) {
        return new SmartDateFormatter(parts);
    }

    public boolean support(String str) {
        int len = str.length();
        if (len >= this.min && len <= this.max) {
            int temp = 0;
            Part[] var4 = this.parts;
            int var5 = var4.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                Part part = var4[var6];
                if (!part.test(str, temp)) {
                    return false;
                }

                temp += part.length();
            }

            return true;
        } else {
            return false;
        }
    }

    public Date format(String str) {
        TemporalAccessor accessor = this.pattern.parse(str);
        LocalDate date = (LocalDate)accessor.query(TemporalQueries.localDate());
        LocalTime time = (LocalTime)accessor.query(TemporalQueries.localTime());
        ZoneId zoneId = (ZoneId)accessor.query(TemporalQueries.zone());
        ZoneOffset offset = (ZoneOffset)accessor.query(TemporalQueries.offset());
        LocalDateTime dateTime;
        if (date != null) {
            if (time != null) {
                dateTime = LocalDateTime.of(date, time);
            } else {
                dateTime = LocalDateTime.of(date, LocalTime.of(0, 0, 0, 0));
            }
        } else {
            dateTime = LocalDateTime.of(LocalDate.now(), time);
        }

        if (offset != null) {
            return Date.from(dateTime.atOffset(offset).toInstant());
        } else {
            return zoneId != null ? Date.from(dateTime.atZone(zoneId).withZoneSameInstant(ZoneId.systemDefault()).toInstant()) : Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
        }
    }

    public String toString(Date date) {
        return this.pattern.format(ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()));
    }

    public String getPattern() {
        return this.patternString;
    }

    public static Part str(String str) {
        return new StringPart(str);
    }

    interface Part {
        int length();

        String pattern();

        boolean test(String var1, int var2);
    }

    static class MatchPart implements Part {
        private final Set<String> matched;
        private final String pattern;
        private final int length;

        MatchPart(String pattern, int length, Set<String> matched) {
            this.pattern = pattern;
            this.length = length;
            this.matched = matched;
        }

        public int length() {
            return this.length;
        }

        public String pattern() {
            return this.pattern;
        }

        public boolean test(String str, int offset) {
            return this.matched.contains(str.substring(offset, offset + this.length));
        }
    }

    static class Week extends MatchPart {
        static Set<String> WEEKS = new HashSet(Arrays.asList("星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "周一", "周二", "周三", "周四", "周五", "周六", "周日"));

        Week(String pattern, int length) {
            super(pattern, length, WEEKS);
        }
    }

    static class Month extends MatchPart {
        static Set<String> MONTHS = new HashSet(Arrays.asList("一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"));

        Month(String pattern, int length) {
            super(pattern, length, MONTHS);
        }
    }

    static class NumberPart implements Part {
        private final String pattern;
        private final int length;
        private final int min;
        private final int max;

        NumberPart(String pattern, int length, int min, int max) {
            this.pattern = pattern;
            this.min = min;
            this.max = max;
            this.length = length;
        }

        NumberPart(String pattern, int min, int max) {
            this(pattern, pattern.length(), min, max);
        }

        public int length() {
            return this.length;
        }

        public String pattern() {
            return this.pattern;
        }

        public boolean test(String str, int offset) {
            int val = toNumber(str, offset, this.length);
            return val >= this.min && val <= this.max;
        }
    }

    static class ZoneOffsetPart implements Part {
        ZoneOffsetPart() {
        }

        public int length() {
            return 6;
        }

        public String pattern() {
            return "Z";
        }

        public boolean test(String str, int offset) {
            if (str.charAt(offset) == '+' && str.charAt(offset + 3) == ':') {
                int hour = toNumber(str, offset + 1, 2);
                if (hour >= 0 && hour <= 12) {
                    int minute = toNumber(str, offset + 4, 2);
                    return minute >= 0;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
    }

    static class TimezonePart extends MatchPart {
        static Set<String> TZ = new HashSet(Arrays.asList("CST", "GMT", "UTC"));

        TimezonePart(String pattern, int length) {
            super(pattern, length, TZ);
        }
    }

    static class StringPart implements Part {
        private final String pattern;
        private final int length;

        StringPart(String pattern) {
            this(pattern, pattern.length());
        }

        StringPart(String pattern, int length) {
            this.pattern = pattern;
            this.length = length;
        }

        public int length() {
            return this.length;
        }

        public String pattern() {
            return this.pattern;
        }

        public boolean test(String str, int offset) {
            for(int i = 0; i < this.pattern.length(); ++i) {
                if (str.charAt(offset + i) != this.pattern.charAt(i)) {
                    return false;
                }
            }

            return true;
        }
    }

    static int toNumber(String str, int index, int length) {
        int result = 0;
        int i = length;

        for(int x = 0; i > 0; ++x) {
            char c = str.charAt(index + x);
            if (c < '0' || c > '9') {
                return -1;
            }

            int val = c - 48;
            result += (int)Math.pow(10.0, (double)(i - 1)) * val;
            --i;
        }

        return result;
    }
}
